package com.xxr.mom.ci.common.util;

import com.xxr.mom.ci.common.exception.InterworkingException;
import com.xxr.mom.ci.common.dto.EncryptionDecryptionDto;
import com.xxr.mom.ci.common.enums.InterworkingRetEnum;
import lombok.extern.log4j.Log4j2;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.util.Base64;

/**
 * 加解密工具类
 *
 * @description: EncryptionDecryptionUtil
 * @date: 2024/8/4 14:27
 * @author: natsuki_kining
 * @version: 1.0
 */
@Log4j2
public class EncryptionDecryptionUtil {

    /**
     * 获取签名
     * <p>
     * 参数签名规范
     * 参数签名采用HMAC-MD5 算法，采用 MD5作为散列函数，通过签名密钥（SigSecret）对整个消息主体进行加密，然后采用MD5信息摘要的方式形成新的密文，参数签名要求大写。
     * 参数签名顺序按照消息体顺序拼接后执行，拼接顺序为运营商标识（OperatorID）、参数内容（Data）、时间戳（TimeStamp）、自增序列（Seq）
     *
     * @return
     */
    public static String sign(String autograph, String data) {
        try {
            Mac mac = Mac.getInstance("HmacMD5");
            mac.init(new SecretKeySpec(autograph.getBytes(), "HmacMD5"));
            byte[] bytes = mac.doFinal(data.getBytes());
            return asHex(bytes).toUpperCase();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    private static String asHex(byte[] bytes) {
        int size = bytes.length;
        StringBuilder sb = new StringBuilder(size);
        for (byte aByte : bytes) {
            String code = Integer.toHexString(aByte & 255);
            if ((aByte & 255) < 16) {
                sb.append('0');
            }
            sb.append(code);
        }
        return sb.toString();
    }

    /**
     * 加密
     *
     * @param dataSecret
     * @param dataSecretIv
     * @param data
     * @return
     * @throws GeneralSecurityException
     */
    public static String encrypt(String dataSecret, String dataSecretIv, String data) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(
                Cipher.ENCRYPT_MODE,
                new SecretKeySpec(dataSecret.getBytes(), "AES"),
                new IvParameterSpec(dataSecretIv.getBytes()));
        byte[] bytes = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 加密
     *
     * @param encrypt
     * @param data
     * @return
     * @throws GeneralSecurityException
     */
    public static String encrypt(EncryptionDecryptionDto encrypt, String data) {
        try {
            return EncryptionDecryptionUtil.encrypt(encrypt.getDataSecret(), encrypt.getDataSecretIv(), data);
        } catch (Exception e) {
            log.info("加密失败", e);
            throw new InterworkingException(InterworkingRetEnum.SYSTEM_ERROR);
        }
    }

    /**
     * 解密
     *
     * @param dataSecret
     * @param dataSecretIv
     * @param data
     * @return
     * @throws GeneralSecurityException
     */
    public static String decode(String dataSecret, String dataSecretIv, String data) {
        try {
            byte[] bytes = Base64.getDecoder().decode(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(
                    Cipher.DECRYPT_MODE,
                    new SecretKeySpec(dataSecret.getBytes(), "AES"),
                    new IvParameterSpec(dataSecretIv.getBytes()));
            bytes = cipher.doFinal(bytes);
            return new String(bytes);
        } catch (Exception e) {
            log.info("解密失败", e);
            throw new InterworkingException(InterworkingRetEnum.DECRYPTION_FAILED);
        }
    }

    public static String decode(EncryptionDecryptionDto decodeDto, String data){
        return decode(decodeDto.getDataSecret(), decodeDto.getDataSecretIv(), data);
    }

    /**
     * 解密并反序列化为指定类型对象
     *
     * @param decodeDto
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Object decodeObject(EncryptionDecryptionDto decodeDto, String data, Class<T> clazz) {
        String decodeString = decode(decodeDto, data);
        if (decodeString.startsWith("[")) {
            return JsonUtil.parseArrayList(decodeString, clazz);
        }
        return JsonUtil.parseObject(decodeString, clazz);
    }

    public static String getSeq() {
        return "0001";
    }

}
